<!--  -->
<template>
  <div class="navIndex-container">
    <div class="header-box"><div>G6大屏显示</div></div>
    <div id="container"></div>
  </div>
</template>

<script>
import { dbList } from "@/store/db"
// import { debounce } from "../utils/tools"
import G6 from "@antv/g6"
import { debounce } from "lodash"

export default {
  data() {
    return {
      graph: null,
      caseUrl: "",
      total: 0
    }
  },
  created() {},
  mounted() {
    this.init()
  },

  methods: {
    init() {
      this.createBgNode()
      this.createRootNode()
      this.createLineArrow()
      this.createExpandNode()
      this.createNode()
      this.initGraph()
      this.renderData()
    },
    initGraph() {
      const width = window.innerWidth
      const height = window.innerHeight - 104
      this.graph = new G6.Graph({
        container: "container",
        width: width,
        height: height,
        modes: { default: ["drag-node", "drag-canvas", "zoom-canvas"] },
        defaultEdge: {
          type: "image-arrow-edge"
        },
        defaultNode: {
          type: "image-node-left",
          anchorPoints: [
            [0, 0.5],
            [1, 0.5]
          ]
        }
      })
    },
    createLineArrow() {
      G6.registerEdge("arrow-right", {
        draw(cfg, group) {
          const { startPoint, endPoint } = cfg
          const offset = 20
          // 1. 添加线条
          const line = group.addShape("path", {
            attrs: {
              path: [
                ["M", startPoint.x, startPoint.y],
                ["L", (startPoint.x + endPoint.x) / 2, startPoint.y],
                ["L", (startPoint.x + endPoint.x) / 2, endPoint.y],
                ["L", endPoint.x - offset, endPoint.y]
              ],
              stroke: "l(0) 0:#112c4d 1:#112947",
              lineWidth: 6
            },
            name: "line-shape"
          })
          const arrowSize = 15
          // 2. 添加箭头图片
          group.addShape("image", {
            attrs: {
              x: endPoint.x - arrowSize / 2 - offset + 5, // 箭头位置调整
              y: endPoint.y - arrowSize / 2,
              width: arrowSize,
              height: arrowSize,
              img: "./static/img/arrow-right.svg" // 可换为 base64
            },
            name: "arrow-img"
          })

          return line
        }
      })
      G6.registerEdge("arrow-left", {
        draw(cfg, group) {
          const { startPoint, endPoint } = cfg
          const offset = 20
          // 1. 添加线条
          const line = group.addShape("path", {
            attrs: {
              path: [
                ["M", startPoint.x, startPoint.y],
                ["L", (startPoint.x + endPoint.x) / 2, startPoint.y],
                ["L", (startPoint.x + endPoint.x) / 2, endPoint.y],
                ["L", endPoint.x + offset, endPoint.y]
              ],
              stroke: "l(0) 0:#112c4d 1:#112947",
              lineWidth: 6
            },
            name: "line-shape"
          })
          const arrowSize = 15
          group.addShape("image", {
            attrs: {
              x: endPoint.x - arrowSize / 2 + offset - 5, // 箭头位置调整
              y: endPoint.y - arrowSize / 2,
              width: arrowSize,
              height: arrowSize,
              img: "./static/img/arrow-left.svg" // 可换为 base64
            },
            name: "arrow-img"
          })

          return line
        }
      })
    },

    createBgNode() {
      G6.registerNode("bg-node", {
        draw(cfg, group) {
          const h = 337
          const width = window.innerWidth
          const bgW = width / 3
          group.addShape("image", {
            attrs: {
              x: -292 / 2 - 20,
              y: -220 / 2,
              width: 292,
              height: 220,
              img: "./static/img/middleBg1.png" // 支持 base64 或 URL
            },
            capture: false,

            name: "background-middleBg1"
          })

          group.addShape("image", {
            attrs: {
              x: -199 / 2,
              y: h / 2,
              width: 199,
              height: 48,
              cursor: "pointer",
              img: "./static/img/btn.png" // 支持 base64 或 URL
            },
            // capture: false,
            name: "btn-image"
          })
          const height = window.innerHeight - 104
          group.addShape("image", {
            attrs: {
              x: -bgW / 2 - cfg.x / 2 - 100,
              y: -height / 2 + height * 0.04,
              height: height * 0.92,
              width: width / 3,
              img: "./static/img/left.png" // 支持 base64 或 URL
            },
            capture: false,
            name: "background-image1"
          })

          group.addShape("image", {
            attrs: {
              x: -bgW / 2 + cfg.x / 2 + 100,
              y: -height / 2 + height * 0.04,
              height: height * 0.92,
              width: bgW,
              img: "./static/img/right.png" // 支持 base64 或 URL
            },
            capture: false,
            name: "background-image2"
          })

          //文本
          group.addShape("text", {
            attrs: {
              text: "开始采集",
              fill: "#fff",
              fontSize: 22,
              x: 0,
              y: h / 2 + 22,
              textAlign: "center",
              textBaseline: "middle"
            },
            capture: false,
            name: "label"
          })

          //全部数据(万条
          group.addShape("text", {
            attrs: {
              text: "全部数据(条)",
              fill: "#fff",
              fontSize: 18,
              textAlign: "center",
              x: 0,
              y: -h / 2 - 50
            },
            name: "title1"
          })
          group.addShape("text", {
            attrs: {
              text: cfg.total,
              fill: "#fff",
              fontSize: 36,
              fontWeight: 900,
              textAlign: "center",
              x: 0,
              y: -h / 2
            },
            name: "title2"
          })

          return group
        },
        update(cfg, node) {
          const height = window.innerHeight - 104
          const group = node.getContainer()
          const background1 = group.find(
            (element) => element.get("name") === "background-image1"
          )
          const background2 = group.find(
            (element) => element.get("name") === "background-image2"
          )

          const middleBg = group.find(
            (element) => element.get("name") === "middleBg"
          )
          background1 &&
            background1.attr({
              y: -height / 2 + height * 0.04,
              height: height * 0.92
            })
          background2 &&
            background2.attr({
              y: -height / 2 + height * 0.04,
              height: height * 0.92
            })
        }
      })
    },
    createExpandNode() {
      G6.registerNode("expand-node-left", {
        draw(cfg, group) {
          const padding = 20
          const width = G6.Util.getTextSize(cfg.label, 14)[0] + padding
          const height = 25
          group.addShape("rect", {
            attrs: {
              x: -width,
              y: -height / 2,
              width: width,
              height: height,
              fill: "rgb(12, 70, 116)",
              radius: 2
            },
            name: "rect-node"
          })

          group.addShape("text", {
            attrs: {
              text: cfg.label,
              fill: "#fff",
              fontSize: 14,
              x: -width / 2,
              y: 0,
              textAlign: "center",
              textBaseline: "middle"
            },
            name: "label"
          })

          return group
        }
      })
      G6.registerNode("expand-node-right", {
        draw(cfg, group) {
          const padding = 20
          const width = G6.Util.getTextSize(cfg.label, 14)[0] + padding
          const height = 25
          group.addShape("rect", {
            attrs: {
              x: 0,
              y: -height / 2,
              width: width,
              height: height,
              fill: "rgb(12, 70, 116)",
              radius: 2
            },
            name: "rect-node"
          })

          group.addShape("text", {
            attrs: {
              text: cfg.label,
              fill: "#fff",
              fontSize: 14,
              x: width / 2,
              y: 0,
              textAlign: "center",
              textBaseline: "middle"
            },
            name: "label"
          })

          return group
        }
      })
    },
    createNode() {
      G6.registerNode("image-node-left", {
        draw(cfg, group) {
          const size = cfg.size || [100, 40]
          const padding = 20
          const textShape = group.addShape("text", {
            attrs: {
              text: cfg.label,
              fill: "#fff",
              fontSize: 14,
              x: -padding,
              y: 5,
              textAlign: "right",
              textBaseline: "middle"
            },
            name: "label"
          })
          const bbox = textShape.getBBox()
          const width = bbox.width + padding * 2 + 20
          const height = 40
          // 背景图片
          const rectImg = group.addShape("image", {
            attrs: {
              x: -width,
              y: -height / 2,
              width: width,
              height: height,
              img: cfg.img // 支持 base64 或 URL
            },
            name: "background-image"
          })
          if (cfg.isChild) {
            group.addShape("image", {
              attrs: {
                x: -width - 25,
                y: -12,
                width: 18,
                height: 18,
                cursor: "pointer",
                img: cfg.collapsed
                  ? "./static/img/close.png"
                  : "./static/img/expand.png" // 支持 base64 或 URL
              },
              name: "expand-image"
            })
          }
          group.toBack(rectImg)

          return group
        }
      })
      G6.registerNode("image-node-right", {
        draw(cfg, group) {
          const padding = 20
          const textShape = group.addShape("text", {
            attrs: {
              text: cfg.label,
              fill: "#fff",
              fontSize: 14,
              x: padding,
              y: 5,
              textAlign: "left",
              textBaseline: "middle"
            },
            name: "label"
          })
          const bbox = textShape.getBBox()
          const width = bbox.width + padding * 2 + 20
          const height = 40
          // 背景图片
          const rectImg = group.addShape("image", {
            attrs: {
              x: 0,
              y: -height / 2,
              width: width,
              height: height,
              img: cfg.img // 支持 base64 或 URL
            },
            name: "background-image"
          })
          if (cfg.isChild) {
            group.addShape("image", {
              attrs: {
                x: width + 10,
                y: -12,
                width: 18,
                height: 18,
                cursor: "pointer",
                img: cfg.collapsed
                  ? "./static/img/close.png"
                  : "./static/img/expand.png" // 支持 base64 或 URL
              },
              name: "expand-image"
            })
          }
          group.toBack(rectImg)

          return group
        }
      })
    },

    createRootNode() {
      G6.registerNode("root-node", {
        draw(cfg, group) {
          const w = 522
          const h = 337
          // 背景图片
          group.addShape("image", {
            attrs: {
              x: -w / 2,
              y: -h / 2 - 5,
              width: w, // 100
              height: h, // 40
              img: "./static/img/middleBg2.png" // 支持 base64 或 URL
            },
            name: "background-image"
          })

          return group
        }
      })
    },

    renderData() {
      this.getData()
      this.listenNodeClick()
      window.addEventListener("resize", () => {
        if (!this.graph || this.graph.get("destroyed")) return
        const width = window.innerWidth
        const height = window.innerHeight - 104
        this.graph.changeSize(width, height)
        debounce(() => {
          this.graph.updateItem("bg-node", {})
          const treeData = this.layoutTreeData(this.total)
          this.graph.changeData(treeData)
          treeData.nodes.forEach((node) => {
            if (node.collapsed) {
              this.toggleSubtreeVisibility(this.graph, node.id, true)
            }
          })
        }, 300)()
      })
    },
    fadeOutShape(shape, duration = 300, callback) {
      let opacity = 1
      const steps = 30
      const step = opacity / steps
      const interval = duration / steps

      const timer = setInterval(() => {
        opacity -= step
        if (opacity <= 0) {
          shape.attr("opacity", 0)
          shape.hide()
          clearInterval(timer)
          if (callback) callback()
        } else {
          shape.attr("opacity", opacity)
        }
      }, interval)
    },
    fadeInShape(shape, duration = 300) {
      let opacity = 0
      const steps = 30
      const step = 1 / steps
      const interval = duration / steps

      shape.show()
      shape.attr("opacity", 0)

      const timer = setInterval(() => {
        opacity += step
        if (opacity >= 1) {
          shape.attr("opacity", 1)
          clearInterval(timer)
        } else {
          shape.attr("opacity", opacity)
        }
      }, interval)
    },
    expandClick(evt) {
      const item = evt.item
      const model = item.getModel()
      const collapsed = !model.collapsed
      model.collapsed = collapsed

      // 1. 更新图标
      const group = item.getContainer()
      const icon = group.find((ele) => ele.get("name") === "expand-image")
      icon.attr(
        "img",
        collapsed ? "./static/img/close.png" : "./static/img/expand.png"
      )

      // 2. 展开当前节点的子树
      this.toggleSubtreeVisibility(this.graph, model.id, collapsed)

      // 3. 如果是展开状态，需要隐藏同方向兄弟节点的子树
      if (collapsed) {
        const parentId = model.parent
        const direction = model.direction

        if (parentId && direction) {
          const siblings = this.graph.getNodes().filter((n) => {
            const m = n.getModel()
            return (
              m.parent === parentId &&
              m.id !== model.id &&
              m.direction === direction
            )
          })

          siblings.forEach((sibling) => {
            const siblingModel = sibling.getModel()
            if (siblingModel.collapsed) {
              siblingModel.collapsed = false

              // 更新图标
              const siblingGroup = sibling.getContainer()
              const siblingIcon = siblingGroup.find(
                (ele) => ele.get("name") === "expand-image"
              )
              siblingIcon && siblingIcon.attr("img", "./static/img/expand.png")

              this.toggleSubtreeVisibility(this.graph, siblingModel.id, false)
            }
          })
        }
      }
    },

    listenNodeClick() {
      this.graph.on("node:click", (evt) => {
        const target = evt.target
        const name = target.get("name")
        if (name == "btn-image") {
          const token = sessionStorage.getItem("token")
          window.open(`${this.caseUrl}?val=${token}`, "_blank")
        }
        if (name == "expand-image") {
          this.expandClick(evt)
        }
      })
    },
    toggleSubtreeVisibility(graph, parentId, visible) {
      const children = graph
        .getNodes()
        .filter((n) => n.getModel().parent === parentId)
      children.forEach((child) => {
        const childId = child.getID()
        const group = child.getContainer()
        const shapes = group.get("children") || []
        if (visible) {
          graph.showItem(child)
          shapes.forEach((shape) => {
            this.fadeInShape(shape)
          })
        } else {
          graph.hideItem(child)
          shapes.forEach((shape) => {
            this.fadeOutShape(shape)
          })
        }

        // 同时隐藏或显示相关边
        graph.getEdges().forEach((edge) => {
          if (
            edge.getModel().source === parentId &&
            edge.getModel().target === childId
          ) {
            const edgeGroup = edge.getContainer()
            const edgeShapes = edgeGroup.get("children") || []
            if (visible) {
              graph.showItem(edge)
              edgeShapes.forEach((shape) => {
                this.fadeInShape(shape)
              })
            } else {
              graph.hideItem(edge)
              edgeShapes.forEach((shape) => {
                this.fadeOutShape(shape)
              })
            }
          }
        })

        // 递归处理子节点
        const model = child.getModel()
        if (visible && !model.collapsed) {
          this.toggleSubtreeVisibility(graph, childId, visible)
        } else if (!visible) {
          this.toggleSubtreeVisibility(graph, childId, visible)
        }
      })
    },

    layoutTreeData(total) {
      const { spacingX, spacingY } = this.getAutoSpacing(
        window.innerWidth,
        window.innerHeight - 104
      )
      const centerX = window.innerWidth / 2
      const centerY = (window.innerHeight - 104) / 2
      const layoutNode = this.layoutRecursiveLeftRight(
        this.nodeList,
        "root",
        centerX,
        centerY,
        spacingX,
        spacingY
      )
      const edges = this.formateEdges(layoutNode)

      const treeGraphData = {
        nodes: [
          {
            id: "bg-node",
            x: centerX,
            y: centerY,
            total,
            type: "bg-node"
          },
          ...layoutNode
        ],
        edges
      }
      return treeGraphData
    },

    getData() {
      const data = dbList
      const nodes = this.formateNodes(data)
      this.total = nodes
        .filter((item) => item.level === 1)
        .reduce((prev, curr) => prev + curr.total, 0)
      const centerX = window.innerWidth / 2
      const centerY = (window.innerHeight - 104) / 2
      this.nodeList = [
        {
          id: "root",
          x: centerX,
          y: centerY,
          anchorPoints: [
            [0, 0.521],
            [1, 0.521]
          ],
          type: "root-node"
        },
        ...nodes
      ]
      const treeGraphData = this.layoutTreeData(this.total)
      this.graph.data(treeGraphData)
      treeGraphData.nodes.forEach((node) => {
        if (node.collapsed) {
          this.toggleSubtreeVisibility(this.graph, node.id, false)
        }
      })
      this.graph.render()
    },
    getAutoSpacing(containerWidth, containerHeight, level = 1) {
      // 可以按实际需求微调下面两个基数

      // 控制 spacing 在最小值和最大值之间
      const spacingX = Math.max(350, containerWidth / 5.5)
      const spacingY = Math.max(50, containerHeight / 16.8)

      return { spacingX, spacingY }
    },
    layoutRecursiveLeftRight(
      nodes,
      rootId = "root",
      centerX = window.innerWidth / 2,
      centerY = (window.innerHeight - 104) / 2,
      baseSpacingX = 350,
      baseSpacingY = 50
    ) {
      const idMap = {}
      nodes.forEach((n) => (idMap[n.id] = n))

      const root = idMap[rootId]
      root.x = centerX
      root.y = centerY

      const placed = new Set([rootId])

      function layoutChildren(parentId, direction, depth = 1, baseY = centerY) {
        const children = nodes.filter(
          (n) => n.parent === parentId && n.direction === direction
        )
        const spacingX = depth > 1 ? baseSpacingX : baseSpacingX
        const spacingY = depth > 1 ? baseSpacingY - 15 : baseSpacingY
        const total = children.length
        const offsetStartY = baseY - ((total - 1) * spacingY) / 2

        children.forEach((child, i) => {
          const x =
            direction === "left"
              ? centerX - spacingX * depth
              : centerX + spacingX * depth
          const y = offsetStartY + i * spacingY

          child.x = x
          child.y = y
          placed.add(child.id)

          // 递归布局子节点
          layoutChildren(child.id, direction, depth + 1, y)
        })
      }

      layoutChildren(rootId, "left")
      layoutChildren(rootId, "right")

      // 返回重新排列后的节点数组（保持原顺序）
      return nodes.map((n) => idMap[n.id])
    },
    formateNodes(data, level = 1, list, parent = "root", direction = "left") {
      if (!list) {
        list = []
      }
      const leftImages = [
        "./static/img/blueLeft.png",
        "./static/img/greenLeft.png",
        "./static/img/redLeft.png",
        "./static/img/orangeLeft.png",
        "./static/img/yellowLeft.png"
      ]
      const rightImages = [
        "./static/img/blueRight.png",
        "./static/img/greenRight.png",
        "./static/img/redRight.png",
        "./static/img/orangeRight.png",
        "./static/img/yellowRight.png"
      ]
      data.forEach((item, index) => {
        if (level == 1) {
          direction = index % 2 === 0 ? "left" : "right"
        }
        let total = item.total
        if (item.children && item.children.length > 0) {
          total = item.children.reduce((pre, cur) => {
            return pre + cur.total
          }, 0)
          this.formateNodes(item.children, level + 1, list, item.id, direction)
        }
        list.push({
          id: String(item.id),
          level: level,
          direction,
          label: `${item.tpmc}（条）：${total}`,
          parent: String(parent),
          total,
          visible: level < 2,
          collapsed: false,
          isChild: item.children && item.children.length > 0 ? true : false,
          anchorPoints: [
            [0, 0.5],
            [1, 0.5]
          ],
          type:
            level == 1 ? `image-node-${direction}` : `expand-node-${direction}`,
          img:
            index % 2 == 0
              ? leftImages[Math.floor(Math.random() * 5)]
              : rightImages[Math.floor(Math.random() * 5)]
        })
      })

      return list
    },
    formateEdges(nodes) {
      const edges = nodes
        .filter((n) => n.parent)
        .map((n) => ({
          source: n.parent,
          target: String(n.id),
          visible: n.level < 2,
          type: `arrow-${n.direction}`
        }))
      return edges
    }
  }
}
</script>
<style lang="less" scoped>
.navIndex-container {
  width: 100%;
  height: 100%;

  background: url("@/assets/img/g6Bg.png") no-repeat;
  background-size: 100% 100%;
  .header-box {
    display: block;
    width: 100%;
    height: 104px;
    background: url("@/assets/img/top.png") no-repeat;
    background-size: 100% 100%;
    font-size: 42px;
    color: #ffffff;
    letter-spacing: 4px;
    text-align: center;
    font-weight: 900;

    > div {
      padding-top: 20px;
      background-image: linear-gradient(180deg, #fff 40%, #b4ddff);
      -webkit-background-clip: text;
      -webkit-text-fill-color: transparent;
    }
  }
  #container {
    height: calc(100vh - 104px);
  }
}
</style>
